package com.ruoyi.system.filter;

import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.core.domain.BaseEntityProps;
import com.ruoyi.common.core.domain.entity.*;
import com.ruoyi.common.core.domain.entity.dto.SysUserCacheDTO;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DataPermission;
import com.ruoyi.common.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.babyfish.jimmer.meta.ImmutableType;
import org.babyfish.jimmer.sql.JSqlClient;
import org.babyfish.jimmer.sql.ast.LikeMode;
import org.babyfish.jimmer.sql.ast.Predicate;
import org.babyfish.jimmer.sql.ast.PropExpression;
import org.babyfish.jimmer.sql.filter.Filter;
import org.babyfish.jimmer.sql.filter.FilterArgs;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Component
@Slf4j
public class AuthDataFilter implements Filter<BaseEntityProps> {

    private final JSqlClient sqlClient;

    /**
     * 忽略权限过滤的表
     */
    private static final List<ImmutableType> IGNORE_AUTH = List.of(
            SysRoleTable.$.getImmutableType()
    );

    @Override
    public void filter(FilterArgs<BaseEntityProps> args) {
        Authentication authentication = SecurityUtils.getAuthentication();
        BaseEntityProps table = args.getTable();
        ImmutableType immutableType = table.getImmutableType();
        if (IGNORE_AUTH.contains(immutableType)) {
            return;
        }
        if (authentication == null) { // 未登录不需过滤
            return;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<SysUserCacheDTO.TargetOf_roles> roles = loginUser.getUser().getRoles();
        if (CollUtil.isEmpty(roles)) {
            return;
        }
        List<String> deptIdList = new ArrayList<>();
        if (SecurityUtils.isAdmin(loginUser.getUserId()) || roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.ALL.getValue()))) {
            return;
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.DEPT_AND_CHILD.getValue()))) {
            String deptId = loginUser.getDeptId();
            deptIdList.add(deptId);
            SysUserCacheDTO.TargetOf_dept dept = loginUser.getUser().getDept();
            SysDeptTable deptTable = SysDeptTable.$;
            List<SysDept> sysDepts = sqlClient.filters(it -> it.disableByTypes(AuthDataFilter.class))
                    .createQuery(deptTable)
                    .where(deptTable.ancestors().likeIf(dept.getAncestors() + "," + deptId, LikeMode.START))
                    .select(deptTable.fetch(
                            SysDeptFetcher.$.allScalarFields()
                    )).execute();
            deptIdList.addAll(sysDepts.stream().map(SysDept::deptId).toList());
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.DEPT.getValue()))) {
            String deptId = loginUser.getDeptId();
            deptIdList.add(deptId);
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.CUSTOM.getValue()))) {
            SysDeptTable dt = SysDeptTable.$;
            List<SysUserCacheDTO.TargetOf_roles> list = roles.stream().filter(a -> a.getDataScope().equals(DataPermission.CUSTOM.getValue())).toList();
            // 获取自定义角色关联的所有部门
            List<SysDept> sysDepts = sqlClient.filters(it -> it.disableByTypes(AuthDataFilter.class))
                    .createQuery(dt)
                    .where(dt.asTableEx().roles().roleId().in(list.stream().map(SysUserCacheDTO.TargetOf_roles::getRoleId).toList()))
                    .select(dt.fetch(SysDeptFetcher.$.allScalarFields()))
                    .execute();
            deptIdList.addAll(sysDepts.stream().map(SysDept::deptId).toList());
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
            args.where(table.createBy().asTableEx().userId().eq(loginUser.getUserId()));
        }
        if (CollUtil.isNotEmpty(deptIdList)) {
            SysDeptTable deptTable = SysDeptTable.$;
            SysUserTable userTable = SysUserTable.$;
            if (deptTable.getImmutableType().equals(immutableType)) {

                if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
                    args.where(
                            Predicate.or(
                                    table.createBy().userId().eq(loginUser.getUserId()),
                                    table.createBy().deptId().in(new ArrayList<>(deptIdList))
                            ));
                } else {
                    args.where(table.get("deptId").in(new ArrayList<>(deptIdList)));
                }
            } else if (userTable.getImmutableType().equals(immutableType)) {
                PropExpression<String> deptId = table.get("deptId");
                if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
                    args.where(
                            Predicate.or(
                                    table.get("userId").eq(loginUser.getUserId()),
                                    deptId.in(deptIdList)
                            ));
                } else {
                    args.where(deptId.in(deptIdList));
                }
            } else {
                if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
                    args.where(
                            Predicate.or(
                                    table.createBy().userId().eq(loginUser.getUserId()),
                                    table.createBy().deptId().in(deptIdList)
                            ));
                } else {
                    args.where(table.createBy().deptId().in(deptIdList));
                }
            }

        }
    }
}
